home *** CD-ROM | disk | FTP | other *** search
/ OpenGL Superbible (2nd Edition) / OpenGL SuperBible e2.iso / tools / Mesa-3.0 / 3Dfx / DEMOS / MESALAND.C < prev    next >
Encoding:
C/C++ Source or Header  |  1998-06-29  |  12.7 KB  |  612 lines

  1. /*
  2.  * This program is under the GNU GPL.
  3.  * Use at your own risk.
  4.  *
  5.  * written by David Bucciarelli (tech.hmw@plus.it)
  6.  *            Humanware s.r.l.
  7.  *
  8.  * based on a Mikael SkiZoWalker's (MoDEL) / France (Skizo@Hol.Fr) demo
  9.  */
  10.  
  11. #include <stdio.h>
  12. #include <math.h>
  13. #include <stdlib.h>
  14. #include <time.h>
  15.  
  16. #ifdef WIN32
  17. #include <windows.h>
  18. #endif
  19.  
  20. #include <GL/glut.h>
  21.  
  22. #ifdef XMESA
  23. #include "GL/xmesa.h"
  24. static int fullscreen=1;
  25. #endif
  26.  
  27. #ifndef M_PI
  28. #define M_PI 3.14159265
  29. #endif
  30.  
  31. #define heightMnt    450
  32. #define    lenghtXmnt    62
  33. #define lenghtYmnt    62
  34.  
  35. #define stepXmnt     96.0
  36. #define stepYmnt     96.0
  37.  
  38. #define WIDTH 640
  39. #define HEIGHT 480
  40.  
  41. #define TSCALE 4
  42.  
  43. #define    FRAME 50
  44.  
  45. #define FOV 85
  46.  
  47. static GLfloat terrain[256*256];
  48. static GLfloat terraincolor[256*256][3];
  49.  
  50. static int win=0;
  51.  
  52. static int fog=1;
  53. static int bfcull=1;
  54. static int usetex=1;
  55. static int poutline=0;
  56. static int help=1;
  57. static int joyavailable=0;
  58. static int joyactive=0;
  59. static float ModZMnt;
  60. static long GlobalMnt=0;
  61.  
  62. static int scrwidth=WIDTH;
  63. static int scrheight=HEIGHT;
  64.  
  65. #define OBSSTARTX 992.0
  66. #define OBSSTARTY 103.0
  67.  
  68. static float obs[3]={OBSSTARTX,heightMnt*1.3,OBSSTARTY};
  69. static float dir[3],v1[2],v2[2];
  70. static float v=0.0;
  71. static float alpha=75.0;
  72. static float beta=90.0;
  73.  
  74. static float gettime(void)
  75. {
  76.   static clock_t told=0;
  77.   clock_t tnew,ris;
  78.  
  79.   tnew=clock();
  80.  
  81.   ris=tnew-told;
  82.  
  83.   told=tnew;
  84.  
  85.   return(ris/(float)CLOCKS_PER_SEC);
  86. }
  87.  
  88. static void calcposobs(void)
  89. {
  90.   float alpha1,alpha2;
  91.  
  92.   dir[0]=sin(alpha*M_PI/180.0);
  93.   dir[2]=cos(alpha*M_PI/180.0)*sin(beta*M_PI/180.0);
  94.   dir[1]=cos(beta*M_PI/180.0);
  95.  
  96.   alpha1=alpha+FOV/2.0;
  97.   v1[0]=sin(alpha1*M_PI/180.0);
  98.   v1[1]=cos(alpha1*M_PI/180.0);
  99.  
  100.   alpha2=alpha-FOV/2.0;
  101.   v2[0]=sin(alpha2*M_PI/180.0);
  102.   v2[1]=cos(alpha2*M_PI/180.0);
  103.   
  104.   obs[0]+=v*dir[0];
  105.   obs[1]+=v*dir[1];
  106.   obs[2]+=v*dir[2];
  107.  
  108.   if(obs[1]<0.0)
  109.     obs[1]=0.0;
  110. }
  111.  
  112. static void reshape( int width, int height )
  113. {
  114.   scrwidth=width;
  115.   scrheight=height;
  116.   glViewport(0, 0, (GLint)width, (GLint)height);
  117.   glMatrixMode(GL_PROJECTION);
  118.   glLoadIdentity();
  119.   gluPerspective(50.0, ((GLfloat) width/(GLfloat)height), lenghtXmnt*stepYmnt*0.01,
  120.          lenghtXmnt*stepYmnt*0.7);
  121.   glMatrixMode(GL_MODELVIEW);
  122.   glLoadIdentity();
  123. }
  124.  
  125. int clipstrip(float y, float *start, float *end)
  126. {
  127.   float x1,x2,t1,t2,tmp;
  128.  
  129.   if(v1[1]==0.0) {
  130.     t1=0.0;
  131.     x1=-HUGE_VAL;
  132.   } else {
  133.     t1=y/v1[1];
  134.     x1=t1*v1[0];
  135.   }
  136.  
  137.   if(v2[1]==0.0) {
  138.     t2=0.0;
  139.     x2=HUGE_VAL;
  140.   } else {
  141.     t2=y/v2[1];
  142.     x2=t2*v2[0];
  143.   }
  144.  
  145.   if(((x1<-(lenghtXmnt*stepXmnt)/2) && (t2<=0.0)) ||
  146.      ((t1<=0.0) && (x2>(lenghtXmnt*stepXmnt)/2)) ||
  147.      ((t1<0.0) && (t2<0.0)))
  148.     return 0;
  149.  
  150.   if((t1==0.0) && (t2==0.0)) {
  151.     if((v1[0]<0.0) && (v1[1]>0.0) && (v2[0]<0.0) && (v2[1]<0.0)) {
  152.       *start=-(lenghtXmnt*stepXmnt)/2;
  153.       *end=stepXmnt;
  154.       return 1;
  155.     } else {
  156.       if((v1[0]>0.0) && (v1[1]<0.0) && (v2[0]>0.0) && (v2[1]>0.0)) {
  157.     *start=-stepXmnt;
  158.     *end=(lenghtXmnt*stepXmnt)/2;
  159.     return 1;
  160.       } else
  161.     return 0;
  162.     }
  163.   } else {
  164.     if(t2<0.0) {
  165.       if(x1<0.0)
  166.     x2=-(lenghtXmnt*stepXmnt)/2;
  167.       else
  168.     x2=(lenghtXmnt*stepXmnt)/2;
  169.     }
  170.  
  171.     if(t1<0.0) {
  172.       if(x2<0.0)
  173.     x1=-(lenghtXmnt*stepXmnt)/2;
  174.       else
  175.     x1=(lenghtXmnt*stepXmnt)/2;
  176.     }
  177.   }
  178.  
  179.   if(x1>x2) {
  180.     tmp=x1;
  181.     x1=x2;
  182.     x2=tmp;
  183.   }
  184.  
  185.   x1-=stepXmnt;
  186.   if(x1<-(lenghtXmnt*stepXmnt)/2)
  187.     x1=-(lenghtXmnt*stepXmnt)/2;
  188.  
  189.   x2+=stepXmnt;
  190.   if(x2>(lenghtXmnt*stepXmnt)/2)
  191.     x2=(lenghtXmnt*stepXmnt)/2;    
  192.  
  193.   *start=((int)(x1/stepXmnt))*stepXmnt;
  194.   *end=((int)(x2/stepXmnt))*stepXmnt;
  195.  
  196.   return 1;
  197. }
  198.  
  199. static void printstring(void *font, char *string)
  200. {
  201.   int len,i;
  202.  
  203.   len=(int)strlen(string);
  204.   for(i=0;i<len;i++)
  205.     glutBitmapCharacter(font,string[i]);
  206. }
  207.  
  208. static void printhelp(void)
  209. {
  210.   glEnable(GL_BLEND);
  211.   glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
  212.   glColor4f(0.0,0.0,0.0,0.5);
  213.   glRecti(40,40,600,440);
  214.   glDisable(GL_BLEND);
  215.  
  216.   glColor3f(1.0,0.0,0.0);
  217.   glRasterPos2i(300,420);
  218.   printstring(GLUT_BITMAP_TIMES_ROMAN_24,"Help");
  219.  
  220.   glRasterPos2i(60,390);
  221.   printstring(GLUT_BITMAP_TIMES_ROMAN_24,"h - Togle Help");
  222.   glRasterPos2i(60,360);
  223.   printstring(GLUT_BITMAP_TIMES_ROMAN_24,"t - Togle Textures");
  224.   glRasterPos2i(60,330);
  225.   printstring(GLUT_BITMAP_TIMES_ROMAN_24,"f - Togle Fog");
  226.   glRasterPos2i(60,300);
  227.   printstring(GLUT_BITMAP_TIMES_ROMAN_24,"p - Wire frame");
  228.   glRasterPos2i(60,270);
  229.   printstring(GLUT_BITMAP_TIMES_ROMAN_24,"b - Togle Back face culling");
  230.   glRasterPos2i(60,240);
  231.   printstring(GLUT_BITMAP_TIMES_ROMAN_24,"Arrow Keys - Rotate");
  232.   glRasterPos2i(60,210);
  233.   printstring(GLUT_BITMAP_TIMES_ROMAN_24,"a - Increase velocity");
  234.   glRasterPos2i(60,180);
  235.   printstring(GLUT_BITMAP_TIMES_ROMAN_24,"z - Decrease velocity");
  236.  
  237.   glRasterPos2i(60,150);
  238.   if(joyavailable)
  239.     printstring(GLUT_BITMAP_TIMES_ROMAN_24,"j - Togle jostick control (Joystick control available)");
  240.   else
  241.     printstring(GLUT_BITMAP_TIMES_ROMAN_24,"(No Joystick control available)");
  242. }
  243.  
  244. void drawterrain(void)
  245. {
  246.   int h,i,idx,ox,oy;
  247.   float j,k,start,end;
  248.  
  249.   ox=(int)(obs[0]/stepXmnt);
  250.   oy=(int)(obs[2]/stepYmnt);
  251.   GlobalMnt=((ox*TSCALE)&255)+((oy*TSCALE)&255)*256;
  252.  
  253.   glPushMatrix();
  254.   glTranslatef((float)ox*stepXmnt,0,(float)oy*stepYmnt);
  255.  
  256.   for(h=0,k=-(lenghtYmnt*stepYmnt)/2;h<lenghtYmnt;k+=stepYmnt,h++) {
  257.     if(!clipstrip(k,&start,&end))
  258.       continue;
  259.  
  260.     glBegin(GL_TRIANGLE_STRIP); /* I hope that the optimizer will be able to improve this code */
  261.     for(i=(int)(lenghtXmnt/2+start/stepXmnt),j=start;j<=end;j+=stepXmnt,i++) {
  262.       idx=(i*TSCALE+h*256*TSCALE+GlobalMnt)&65535;
  263.       glColor3fv(terraincolor[idx]);
  264.       glTexCoord2f((ox+i)/8.0,(oy+h)/8.0);
  265.       glVertex3f(j,terrain[idx],k);
  266.  
  267.       idx=(i*TSCALE+h*256*TSCALE+256*TSCALE+GlobalMnt)&65535; 
  268.       glColor3fv(terraincolor[idx]);
  269.       glTexCoord2f((ox+i)/8.0,(oy+h+1)/8.0);
  270.       glVertex3f(j,terrain[idx],k+stepYmnt);
  271.     }
  272.     glEnd();
  273.   }
  274.  
  275.   glDisable(GL_CULL_FACE);
  276.   glDisable(GL_TEXTURE_2D);
  277.   glEnable(GL_BLEND);
  278.   glBegin(GL_QUADS);
  279.   glColor4f(0.1,0.7,1.0,0.4);
  280.   glVertex3f(-(lenghtXmnt*stepXmnt)/2.0,heightMnt*0.6,-(lenghtYmnt*stepYmnt)/2.0);
  281.   glVertex3f(-(lenghtXmnt*stepXmnt)/2.0,heightMnt*0.6,(lenghtYmnt*stepYmnt)/2.0);
  282.   glVertex3f((lenghtXmnt*stepXmnt)/2.0,heightMnt*0.6,(lenghtYmnt*stepYmnt)/2.0);
  283.   glVertex3f((lenghtXmnt*stepXmnt)/2.0,heightMnt*0.6,-(lenghtYmnt*stepYmnt)/2.0);
  284.   glEnd();
  285.   glDisable(GL_BLEND);
  286.   if(bfcull)
  287.     glEnable(GL_CULL_FACE);
  288.   glEnable(GL_TEXTURE_2D);
  289.  
  290.   glPopMatrix();
  291.  
  292. }
  293.  
  294. static void dojoy(void)
  295. {
  296. #ifdef WIN32
  297.   static UINT max[2]={0,0};
  298.   static UINT min[2]={0xffffffff,0xffffffff},center[2];
  299.   MMRESULT res;
  300.   JOYINFO joy;
  301.  
  302.   res=joyGetPos(JOYSTICKID1,&joy);
  303.  
  304.   if(res==JOYERR_NOERROR) {
  305.     joyavailable=1;
  306.  
  307.     if(max[0]<joy.wXpos)
  308.       max[0]=joy.wXpos;
  309.     if(min[0]>joy.wXpos)
  310.       min[0]=joy.wXpos;
  311.     center[0]=(max[0]+min[0])/2;
  312.  
  313.     if(max[1]<joy.wYpos)
  314.       max[1]=joy.wYpos;
  315.     if(min[1]>joy.wYpos)
  316.       min[1]=joy.wYpos;
  317.     center[1]=(max[1]+min[1])/2;
  318.  
  319.     if(joyactive) {
  320.       if(fabs(center[0]-(float)joy.wXpos)>0.1*(max[0]-min[0]))
  321.     alpha+=2.5*(center[0]-(float)joy.wXpos)/(max[0]-min[0]);
  322.       if(fabs(center[1]-(float)joy.wYpos)>0.1*(max[1]-min[1]))
  323.     beta+=2.5*(center[1]-(float)joy.wYpos)/(max[1]-min[1]);
  324.  
  325.       if(joy.wButtons & JOY_BUTTON1)
  326.     v+=0.5;
  327.       if(joy.wButtons & JOY_BUTTON2)
  328.     v-=0.5;
  329.     }
  330.   } else
  331.     joyavailable=0;
  332. #endif
  333. }
  334.  
  335. void drawscene(void)
  336. {
  337.   static int count=0;
  338.   static char frbuf[80];
  339.   float fr;
  340.  
  341.   dojoy();
  342.  
  343.   glShadeModel(GL_SMOOTH);
  344.   glEnable(GL_DEPTH_TEST);
  345.  
  346.   if(usetex)
  347.     glEnable(GL_TEXTURE_2D);
  348.   else
  349.     glDisable(GL_TEXTURE_2D);
  350.  
  351.   if(fog)
  352.     glEnable(GL_FOG);
  353.   else
  354.     glDisable(GL_FOG);
  355.  
  356.   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  357.  
  358.   glPushMatrix();
  359.  
  360.   calcposobs();
  361.   gluLookAt(obs[0],obs[1],obs[2],
  362.         obs[0]+dir[0],obs[1]+dir[1],obs[2]+dir[2],
  363.         0.0,1.0,0.0);
  364.  
  365.   drawterrain();
  366.   glPopMatrix();
  367.  
  368.   if((count % FRAME)==0) {
  369.     fr=gettime();
  370.     sprintf(frbuf,"Frame rate: %.3f",FRAME/fr);
  371.   }
  372.  
  373.   glDisable(GL_TEXTURE_2D);
  374.   glDisable(GL_DEPTH_TEST);
  375.   glDisable(GL_FOG);
  376.   glShadeModel(GL_FLAT);
  377.  
  378.   glMatrixMode(GL_PROJECTION);
  379.   glLoadIdentity();
  380.   glOrtho(-0.5,639.5,-0.5,479.5,-1.0,1.0);
  381.   glMatrixMode(GL_MODELVIEW);
  382.   glLoadIdentity();
  383.  
  384.   glColor3f(1.0,0.0,0.0);
  385.   glRasterPos2i(10,10);
  386.   printstring(GLUT_BITMAP_HELVETICA_18,frbuf);
  387.   glRasterPos2i(350,470);
  388.   printstring(GLUT_BITMAP_HELVETICA_10,"Terrain V1.2 Written by David Bucciarelli (tech.hmw@plus.it)");
  389.   glRasterPos2i(434,457);
  390.   printstring(GLUT_BITMAP_HELVETICA_10,"Based on a Mickael's demo (Skizo@Hol.Fr)");
  391.  
  392.   if(help)
  393.     printhelp();
  394.  
  395.   reshape(scrwidth,scrheight);
  396.  
  397.   glutSwapBuffers();
  398.  
  399.   count++;
  400. }
  401.  
  402. static void key(unsigned char k, int x, int y)
  403. {
  404.   switch (k) {
  405.   case 27:
  406.     exit(0);
  407.     break;
  408.   case 'a':
  409.     v+=0.5;
  410.     break;
  411.   case 'z':
  412.     v-=0.5;
  413.     break;
  414.   case 'p':
  415.     if(poutline) {
  416.       glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
  417.       poutline=0;
  418.     }    else {
  419.       glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
  420.       poutline=1;
  421.     }
  422.     break;
  423.   case 'j':
  424.     joyactive=(!joyactive);
  425.     break;
  426.   case 'h':
  427.     help=(!help);
  428.     break;
  429.   case 'f':
  430.     fog=(!fog);
  431.     break;
  432.   case 't':
  433.     usetex=(!usetex);
  434.     break;
  435.   case 'b':
  436.     if(bfcull) {
  437.       glDisable(GL_CULL_FACE);
  438.       bfcull=0;
  439.     } else {
  440.       glEnable(GL_CULL_FACE);
  441.       bfcull=1;
  442.     }
  443.     break;
  444. #ifdef XMESA
  445.   case ' ':
  446.     XMesaSetFXmode(fullscreen ? XMESA_FX_FULLSCREEN : XMESA_FX_WINDOW);
  447.     fullscreen=(!fullscreen);
  448.     break;
  449. #endif
  450.   }
  451. }
  452.  
  453. static void special(int k, int x, int y)
  454. {
  455.   switch(k) {
  456.   case GLUT_KEY_LEFT:
  457.     alpha+=2.0;
  458.     break;
  459.   case GLUT_KEY_RIGHT:
  460.     alpha-=2.0;
  461.     break;
  462.   case GLUT_KEY_DOWN:
  463.     beta-=2.0;
  464.     break;
  465.   case GLUT_KEY_UP:
  466.     beta+=2.0;
  467.     break;
  468.   }
  469. }
  470.  
  471. static void calccolor(GLfloat height, GLfloat c[3])
  472. {
  473.   GLfloat color[4][3]={
  474.     {1.0,1.0,1.0},
  475.     {0.0,0.8,0.0},
  476.     {1.0,1.0,0.3},
  477.     {0.0,0.0,0.8}
  478.   };
  479.   GLfloat fact;
  480.  
  481.   height=height*(1.0/255.0);
  482.  
  483.   if(height>=0.9) {
  484.     c[0]=color[0][0]; c[1]=color[0][1]; c[2]=color[0][2];
  485.     return;
  486.   }
  487.  
  488.   if((height<0.9) && (height>=0.7)) {
  489.     fact=(height-0.7)*5.0;
  490.     c[0]=fact*color[0][0]+(1.0-fact)*color[1][0];
  491.     c[1]=fact*color[0][1]+(1.0-fact)*color[1][1];
  492.     c[2]=fact*color[0][2]+(1.0-fact)*color[1][2];
  493.     return;
  494.   }
  495.  
  496.   if((height<0.7) && (height>=0.6)) {
  497.     fact=(height-0.6)*10.0;
  498.     c[0]=fact*color[1][0]+(1.0-fact)*color[2][0];
  499.     c[1]=fact*color[1][1]+(1.0-fact)*color[2][1];
  500.     c[2]=fact*color[1][2]+(1.0-fact)*color[2][2];
  501.     return;
  502.   }
  503.  
  504.   if((height<0.6) && (height>=0.5)) {
  505.     fact=(height-0.5)*10.0;
  506.     c[0]=fact*color[2][0]+(1.0-fact)*color[3][0];
  507.     c[1]=fact*color[2][1]+(1.0-fact)*color[3][1];
  508.     c[2]=fact*color[2][2]+(1.0-fact)*color[3][2];
  509.     return;
  510.   }
  511.  
  512.   c[0]=color[3][0]; c[1]=color[3][1]; c[2]=color[3][2];
  513. }
  514.  
  515. static void loadpic (void)
  516. {
  517.   GLubyte bufferter[256*256],terrainpic[256*256];
  518.   FILE *FilePic;
  519.   int i,tmp;
  520.   GLenum gluerr;
  521.  
  522.   if((FilePic=fopen("mnt.bin","r"))==NULL) {
  523.     fprintf(stderr,"Error loading Mnt.bin\n");
  524.     exit(-1);
  525.   }
  526.   fread(bufferter , 256*256 , 1 , FilePic);
  527.   fclose(FilePic);
  528.  
  529.   for (i=0;i<(256*256);i++) {
  530.     terrain[i]=(bufferter[i]*(heightMnt/255.0f));
  531.     calccolor((GLfloat)bufferter[i],terraincolor[i]);
  532.     tmp=(((int)bufferter[i])+96);
  533.     terrainpic[i]=(tmp>255) ? 255 : tmp;
  534.   }
  535.  
  536.   glPixelStorei(GL_UNPACK_ALIGNMENT,1);
  537.   if((gluerr=gluBuild2DMipmaps(GL_TEXTURE_2D, 1, 256, 256, GL_LUMINANCE,
  538.                    GL_UNSIGNED_BYTE, (GLvoid *)(&terrainpic[0])))) {
  539.     fprintf(stderr,"GLULib%s\n",gluErrorString(gluerr));
  540.     exit(-1);
  541.   }
  542.  
  543.   glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
  544.   glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
  545.  
  546.   glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
  547.   glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
  548.  
  549.   glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
  550.   glEnable(GL_TEXTURE_2D);
  551. }
  552.  
  553. static void init( void )
  554. {
  555.   float fogcolor[4]={0.6,0.7,0.7,1.0};
  556.  
  557.   glClearColor(fogcolor[0],fogcolor[1],fogcolor[2],fogcolor[3]);
  558.   glClearDepth(1.0);
  559.   glDepthFunc(GL_LEQUAL);
  560.   glShadeModel(GL_SMOOTH);
  561.   glEnable(GL_DEPTH_TEST);
  562.   glEnable(GL_CULL_FACE);
  563.  
  564.   glDisable(GL_BLEND);
  565.   glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
  566.  
  567.   glEnable(GL_FOG);
  568.   glFogi(GL_FOG_MODE,GL_EXP2);
  569.   glFogfv(GL_FOG_COLOR,fogcolor);
  570.   glFogf(GL_FOG_DENSITY,0.0007);
  571. #ifdef FX
  572.   glHint(GL_FOG_HINT,GL_NICEST);
  573. #endif
  574.  
  575.   reshape(scrwidth,scrheight);
  576. }
  577.  
  578.  
  579. int main(int ac, char **av)
  580. {
  581.   glutInitWindowPosition(0,0);
  582.   glutInitWindowSize(WIDTH,HEIGHT);
  583.   glutInit(&ac,av);
  584.  
  585.   glutInitDisplayMode(GLUT_RGB|GLUT_DEPTH|GLUT_DOUBLE);
  586.  
  587.   if(!(win=glutCreateWindow("Terrain"))) {
  588.     fprintf(stderr,"Error, couldn't open window\n");
  589.     return -1;
  590.   }
  591.  
  592.   ModZMnt = 0.0f;
  593.   loadpic();
  594.  
  595.   init();
  596.  
  597. #ifndef FX
  598.   glDisable(GL_TEXTURE_2D);
  599.   usetex=0;
  600. #endif
  601.  
  602.   glutReshapeFunc(reshape);
  603.   glutDisplayFunc(drawscene);
  604.   glutKeyboardFunc(key);
  605.   glutSpecialFunc(special);
  606.   glutIdleFunc(drawscene);
  607.  
  608.   glutMainLoop();
  609.  
  610.   return 0;
  611. }
  612.